package indigo.core;

import java.io.IOException;
import java.util.Set;

import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

import com.google.common.collect.Sets;

public class ResourceLoaderClassScanner {
	private final ResourcePatternResolver resourcePatternResolver;

	public ResourceLoaderClassScanner(ResourceLoader resourceLoader) {
		this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
	}

	private boolean matchesFilter(TypeFilter[] filters, MetadataReader reader,
			MetadataReaderFactory readerFactory) throws IOException {
		for (TypeFilter filter : filters) {
			if (filter.match(reader, readerFactory)) {
				return true;
			}
		}
		return false;
	}

	private Set<String> getClassNames(String packageName, String pattern, TypeFilter[] filters) throws IOException {
		String locationPattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
				+ ClassUtils.convertClassNameToResourcePath(packageName)
				+ pattern;
		Resource[] resources = resourcePatternResolver.getResources(locationPattern);
		MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
		Set<String> classNames = Sets.newHashSet();
		for (Resource resource : resources) {
			if (resource.isReadable()) {
				MetadataReader reader = readerFactory.getMetadataReader(resource);
				String className = reader.getClassMetadata().getClassName();
				if (matchesFilter(filters, reader, readerFactory)) {
					classNames.add(className);
				}
			}
		}
		return classNames;
	}

	public Set<String> getClassNamesRecursive(String packageName, TypeFilter... filters) throws IOException {
		return getClassNames(packageName, "/**/*.class", filters);
	}
}
